home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / daemons / lpd / lpd.c < prev    next >
C/C++ Source or Header  |  1992-05-17  |  14KB  |  645 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific prior written permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifndef lint
  14. char copyright[] =
  15. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  16.  All rights reserved.\n";
  17. #endif /* not lint */
  18.  
  19. #ifndef lint
  20. static char sccsid[] = "@(#)lpd.c    5.5 (Berkeley) 5/5/88";
  21. #endif /* not lint */
  22.  
  23. /*
  24.  * lpd -- line printer daemon.
  25.  *
  26.  * Listen for a connection and perform the requested operation.
  27.  * Operations are:
  28.  *    \1printer\n
  29.  *        check the queue for jobs and print any found.
  30.  *    \2printer\n
  31.  *        receive a job from another machine and queue it.
  32.  *    \3printer [users ...] [jobs ...]\n
  33.  *        return the current state of the queue (short form).
  34.  *    \4printer [users ...] [jobs ...]\n
  35.  *        return the current state of the queue (long form).
  36.  *    \5printer person [users ...] [jobs ...]\n
  37.  *        remove jobs from the queue.
  38.  *
  39.  * Strategy to maintain protected spooling area:
  40.  *    1. Spooling area is writable only by daemon and spooling group
  41.  *    2. lpr runs setuid root and setgrp spooling group; it uses
  42.  *       root to access any file it wants (verifying things before
  43.  *       with an access call) and group id to know how it should
  44.  *       set up ownership of files in the spooling area.
  45.  *    3. Files in spooling area are owned by root, group spooling
  46.  *       group, with mode 660.
  47.  *    4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
  48.  *       access files and printer.  Users can't get to anything
  49.  *       w/o help of lpq and lprm programs.
  50.  */
  51.  
  52. #include "lp.h"
  53. #ifdef sprite
  54. #include <fs.h>
  55. #include <sprite.h>
  56. #include <pdev.h>
  57. #include <status.h>
  58. #endif
  59.  
  60. int    lflag;                /* log requests flag */
  61.  
  62. int    reapchild();
  63. int    mcleanup();
  64.  
  65. #ifdef sprite
  66. static ReturnStatus P_Open();
  67. static ReturnStatus P_Close();
  68. static ReturnStatus P_Write();
  69. static int client;
  70. static void RemoteWrite();
  71. Pdev_CallBacks pdevHandlers = {
  72.     P_Open,
  73.     NULL,
  74.     P_Write,
  75.     NULL,
  76.     P_Close,
  77.     NULL,
  78.     NULL
  79. };
  80. #define FINET_ID    47
  81. int debug;
  82. #endif
  83.  
  84.  
  85. main(argc, argv)
  86.     int argc;
  87.     char **argv;
  88. {
  89. #ifdef sprite
  90.     int f, finet, lfd;
  91.     Pdev_Token pdev;
  92. #else
  93.     int f, funix, finet, options, defreadfds, fromlen;
  94.     static struct sockaddr_un sun, fromunix;
  95.     int omask, lfd;
  96. #endif
  97.     static struct sockaddr_in sin, frominet;
  98.     int err;
  99.  
  100.     gethostname(host, sizeof(host));
  101. #ifdef sprite
  102.     sprintf(pdevName, "/hosts/%s/dev/printer", host);
  103. #endif
  104.     name = argv[0];
  105.     while (--argc > 0) {
  106.         argv++;
  107.         if (argv[0][0] == '-')
  108.             switch (argv[0][1]) {
  109.             case 'd':
  110. #ifdef sprite
  111.                     debug = 1;
  112. #else
  113.                 options |= SO_DEBUG;
  114. #endif
  115.                 break;
  116.             case 'l':
  117.                 lflag++;
  118.                 break;
  119.             }
  120.     }
  121.  
  122. #ifndef DEBUG
  123.     /*
  124.      * Set up standard environment by detaching from the parent.
  125.      * Regular file descriptors are closed, stderr is hooked
  126.      * to /dev/syslog.
  127.      */
  128.     if (fork())
  129.         exit(0);
  130.     for (f = 0; f < 5; f++)
  131.         (void) close(f);
  132.     (void) open("/dev/null", O_RDONLY);
  133.     (void) open("/dev/null", O_WRONLY);
  134.     err = open("/dev/syslog", O_WRONLY);
  135.     if (err >= 0 && err != 2) {
  136.         dup2(err, 2);
  137.         close(err);
  138.     }
  139.     f = open("/dev/tty", O_RDWR);
  140.     if (f > 0) {
  141.         ioctl(f, TIOCNOTTY, 0);
  142.         (void) close(f);
  143.     }
  144. #endif
  145.  
  146.     openlog("lpd", LOG_PID, LOG_LPR);
  147.     if (err < 0) {
  148.         syslog(LOG_ERR, "%s: %m", "/dev/syslog open failed");
  149.     }
  150.     (void) umask(0);
  151.     sprintf(masterLock, "/sprite/spool/lpd/%s.lock", host);
  152.     lfd = open(masterLock, O_WRONLY|O_CREAT, 0644);
  153.     if (lfd < 0) {
  154.         syslog(LOG_ERR, "%s: %m", masterLock);
  155.         exit(1);
  156.     }
  157.     if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
  158.         if (errno == EWOULDBLOCK)    /* active deamon present */
  159.             exit(0);
  160.         syslog(LOG_ERR, "%s: %m", masterLock);
  161.         exit(1);
  162.     }
  163.     ftruncate(lfd, 0);
  164.     /*
  165.      * write process id for others to know
  166.      */
  167.     sprintf(line, "%u\n", getpid());
  168.     f = strlen(line);
  169.     if (write(lfd, line, f) != f) {
  170.         syslog(LOG_ERR, "%s: %m", masterLock);
  171.         exit(1);
  172.     }
  173.     signal(SIGCHLD, reapchild);
  174.     /*
  175.      * Restart all the printers.
  176.      */
  177.     startup();
  178. #ifdef sprite
  179.     if ((pdev = Pdev_Open(pdevName, NULL, 0, 0, &pdevHandlers, 0)) == NULL) {
  180.         syslog(LOG_ERR, "Pdev_Open: %s", pdev_ErrorMsg);
  181.         exit(1);
  182.     }
  183.     signal(SIGHUP, mcleanup);
  184.     signal(SIGINT, mcleanup);
  185.     signal(SIGQUIT, mcleanup);
  186.     signal(SIGTERM, mcleanup);
  187. #else
  188.     (void) unlink(SOCKETNAME);
  189.     funix = socket(AF_UNIX, SOCK_STREAM, 0);
  190.     if (funix < 0) {
  191.         syslog(LOG_ERR, "socket: %m");
  192.         exit(1);
  193.     }
  194. #define    mask(s)    (1 << ((s) - 1))
  195.     omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
  196.     signal(SIGHUP, mcleanup);
  197.     signal(SIGINT, mcleanup);
  198.     signal(SIGQUIT, mcleanup);
  199.     signal(SIGTERM, mcleanup);
  200.     sun.sun_family = AF_UNIX;
  201.     strcpy(sun.sun_path, SOCKETNAME);
  202.     if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) {
  203.         syslog(LOG_ERR, "ubind: %m");
  204.         exit(1);
  205.     }
  206.     sigsetmask(omask);
  207.     defreadfds = 1 << funix;
  208.     listen(funix, 5);
  209. #endif
  210.     finet = socket(AF_INET, SOCK_STREAM, 0);
  211.     if (finet >= 0) {
  212.         struct servent *sp;
  213. #ifndef sprite
  214.         if (options & SO_DEBUG)
  215.             if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
  216.                 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
  217.                 mcleanup();
  218.             }
  219. #endif
  220.         sp = getservbyname("printer", "tcp");
  221.         if (sp == NULL) {
  222.             syslog(LOG_ERR, "printer/tcp: unknown service");
  223.             mcleanup();
  224.         }
  225.         sin.sin_family = AF_INET;
  226.         sin.sin_port = sp->s_port;
  227.         if (bind(finet, &sin, sizeof(sin), 0) < 0) {
  228.             syslog(LOG_ERR, "bind: %m");
  229.             mcleanup();
  230.         }
  231. #ifdef sprite
  232.         Fs_EventHandlerCreate(finet, FS_READABLE, RemoteWrite,FINET_ID);
  233. #else
  234.         defreadfds |= 1 << finet;
  235. #endif
  236.         listen(finet, 5);
  237.     }
  238.  
  239. #ifdef sprite
  240.     for (;;)
  241.         Fs_Dispatch();
  242. #else
  243.  
  244.     /*
  245.      * Main loop: accept, do a request, continue.
  246.      */
  247.     for (;;) {
  248.         int domain, nfds, s, readfds = defreadfds;
  249.  
  250.         nfds = select(20, &readfds, 0, 0, 0);
  251.         if (nfds <= 0) {
  252.             if (nfds < 0 && errno != EINTR)
  253.                 syslog(LOG_WARNING, "select: %m");
  254.             continue;
  255.         }
  256.         if (readfds & (1 << funix)) {
  257.             domain = AF_UNIX, fromlen = sizeof(fromunix);
  258.             s = accept(funix, &fromunix, &fromlen);
  259.         } else if (readfds & (1 << finet)) {
  260.             domain = AF_INET, fromlen = sizeof(frominet);
  261.             s = accept(finet, &frominet, &fromlen);
  262.         }
  263.         if (s < 0) {
  264.             if (errno != EINTR)
  265.                 syslog(LOG_WARNING, "accept: %m");
  266.             continue;
  267.         }
  268.         if (fork() == 0) {
  269.             signal(SIGCHLD, SIG_IGN);
  270.             signal(SIGHUP, SIG_IGN);
  271.             signal(SIGINT, SIG_IGN);
  272.             signal(SIGQUIT, SIG_IGN);
  273.             signal(SIGTERM, SIG_IGN);
  274.             (void) close(funix);
  275.             (void) close(finet);
  276.             dup2(s, 1);
  277.             (void) close(s);
  278.             if (domain == AF_INET)
  279.                 chkhost(&frominet);
  280.             doit();
  281.             exit(0);
  282.         }
  283.         (void) close(s);
  284.     }
  285. #endif
  286. }
  287.  
  288. reapchild()
  289. {
  290.     union wait status;
  291.  
  292.     while (wait3(&status, WNOHANG, 0) > 0)
  293.         ;
  294. }
  295.  
  296. mcleanup()
  297. {
  298.     if (lflag)
  299.         syslog(LOG_INFO, "exiting");
  300. #ifdef sprite
  301.     unlink(pdevName);
  302. #else
  303.     unlink(SOCKETNAME);
  304. #endif
  305.     exit(0);
  306. }
  307.  
  308. /*
  309.  * Stuff for handling job specifications
  310.  */
  311. char    *user[MAXUSERS];    /* users to process */
  312. int    users;            /* # of users in user array */
  313. int    requ[MAXREQUESTS];    /* job number of spool entries */
  314. int    requests;        /* # of spool requests */
  315. char    *person;        /* name of person doing lprm */
  316.  
  317. char    fromb[32];    /* buffer for client's machine name */
  318. char    cbuf[BUFSIZ];    /* command line buffer */
  319. char    *cmdnames[] = {
  320.     "null",
  321.     "printjob",
  322.     "recvjob",
  323.     "displayq short",
  324.     "displayq long",
  325.     "rmjob"
  326. };
  327.  
  328. #ifdef sprite
  329.  
  330. static void
  331. RemoteWrite(clientData, streamID, eventMask)
  332.     ClientData clientData;
  333.     int streamID;
  334.     int eventMask;
  335. {
  336.     int r;
  337.     struct sockaddr_in frominet;
  338.     int fromlen;
  339.     int s;
  340.  
  341.     if ((int) clientData != FINET_ID) {
  342.     syslog(LOG_ERR, "RemoteWrite: bad clientData (%d)", (int) clientData);
  343.     return;
  344.     }
  345.     fromlen = sizeof(frominet);
  346.     if ((s = accept(streamID, &frominet, &fromlen)) < 0) {
  347.     syslog(LOG_ERR, "accept: %m");
  348.     return;
  349.     }
  350.     if ((r = read(s, cbuf, sizeof(cbuf))) < 0) {
  351.     syslog(LOG_ERR, "finet read: %m");
  352.     return;
  353.     }
  354.     cbuf[r] = '\0';
  355.     if (cbuf[r - 1] == '\n')
  356.     cbuf[r - 1] = '\0';
  357.     if (debug)
  358.     syslog(LOG_ERR, "RemoteWrite: (%s)", cbuf);
  359.     if (fork() == 0) {
  360.     signal(SIGCHLD, SIG_IGN);
  361.     signal(SIGHUP, SIG_IGN);
  362.     signal(SIGINT, SIG_IGN);
  363.     signal(SIGQUIT, SIG_IGN);
  364.     signal(SIGTERM, SIG_IGN);
  365.     close(streamID);
  366.     dup2(s, 1);
  367.     close(s);
  368.     chkhost(&frominet);
  369.     doit();
  370.     /*NOTREACHED*/
  371.     }
  372.     close(s);
  373.     return;
  374. }
  375.  
  376. static ReturnStatus
  377. P_Open(token, streamPtr, readBuffer, flags, pid, hostID, uid, selectBitsPtr)
  378.     ClientData token;        /* Was passed to Pdev_Open call */
  379.     Pdev_Stream *streamPtr;
  380.     char *readBuffer;
  381.     int flags;
  382.     int pid;
  383.     int hostID;
  384.     int uid;
  385.     int *selectBitsPtr;
  386. {
  387.     if (debug) {
  388.         syslog(LOG_ERR,
  389.         "<%d> Proc %x at host %x, uid %x opened with flags %x\n",
  390.         client, pid, hostID, uid, flags);
  391.     }
  392.     streamPtr->clientData = client;
  393.     client++;
  394.     *selectBitsPtr = FS_WRITABLE;
  395.     return SUCCESS;
  396. }
  397.  
  398. static ReturnStatus
  399. P_Close(streamPtr)
  400.     Pdev_Stream *streamPtr;
  401. {
  402.     if (debug) {
  403.         syslog(LOG_ERR, "<%d> client closed\n",
  404.         (int) streamPtr->clientData);
  405.     }
  406.     return SUCCESS;
  407. }
  408.  
  409. static ReturnStatus
  410. P_Write(streamPtr, async, writePtr, selectBitsPtr, sigPtr)
  411.     Pdev_Stream *streamPtr;
  412.     int async;
  413.     Pdev_RWParam *writePtr;
  414.     int *selectBitsPtr;
  415.     Pdev_Signal *sigPtr;
  416. {
  417.     register int n;
  418.     register char *buffer = writePtr->buffer;
  419.  
  420.     if (debug)
  421.         syslog(LOG_ERR, "Pdev_Write");
  422.     n = writePtr->length;
  423.     buffer[n] = '\0';
  424.     if (buffer[n - 1] == '\n')
  425.         buffer[n - 1] = '\0';
  426.     switch (buffer[0]) {
  427.  
  428.     case 'd':
  429.         debug = 1;
  430.         break;
  431.  
  432.     case 'n':
  433.         debug = 0;
  434.         break;
  435.  
  436.     default:
  437.         if (fork() == 0) {
  438.         signal(SIGCHLD, SIG_IGN);
  439.         signal(SIGHUP, SIG_IGN);
  440.         signal(SIGINT, SIG_IGN);
  441.         signal(SIGQUIT, SIG_IGN);
  442.         signal(SIGTERM, SIG_IGN);
  443.         strcpy(cbuf, buffer);
  444.         doit();
  445.         /*NOTREACHED*/
  446.     }
  447.     }
  448.     return SUCCESS;
  449. }
  450. #endif /* sprite */
  451.  
  452. doit()
  453. {
  454.     register char *cp;
  455.     register int n;
  456.  
  457. #ifdef sprite
  458.     Proc_Detach(0);
  459. #else
  460.     for (;;) {
  461.         cp = cbuf;
  462.         do {
  463.             if (cp >= &cbuf[sizeof(cbuf) - 1])
  464.                 fatal("Command line too long");
  465.             if ((n = read(1, cp, 1)) != 1) {
  466.                 if (n < 0)
  467.                     fatal("Lost connection");
  468.                 return;
  469.             }
  470.         } while (*cp++ != '\n');
  471.         *--cp = '\0';
  472. #endif
  473.         cp = cbuf;
  474.         if (lflag) {
  475.             if (*cp >= '\1' && *cp <= '\5')
  476.                 syslog(LOG_INFO, "%s requests %s %s",
  477.                     from, cmdnames[*cp], cp+1);
  478.             else
  479.                 syslog(LOG_INFO, "bad request (%d) from %s",
  480.                     *cp, from);
  481.         }
  482.         switch (*cp++) {
  483.         case '\1':    /* check the queue and print any jobs there */
  484.             printer = cp;
  485.             printjob();
  486.             break;
  487.         case '\2':    /* receive files to be queued */
  488.             printer = cp;
  489.             recvjob();
  490.             break;
  491.         case '\3':    /* display the queue (short form) */
  492.         case '\4':    /* display the queue (long form) */
  493.             printer = cp;
  494.             while (*cp) {
  495.                 if (*cp != ' ') {
  496.                     cp++;
  497.                     continue;
  498.                 }
  499.                 *cp++ = '\0';
  500.                 while (isspace(*cp))
  501.                     cp++;
  502.                 if (*cp == '\0')
  503.                     break;
  504.                 if (isdigit(*cp)) {
  505.                     if (requests >= MAXREQUESTS)
  506.                         fatal("Too many requests");
  507.                     requ[requests++] = atoi(cp);
  508.                 } else {
  509.                     if (users >= MAXUSERS)
  510.                         fatal("Too many users");
  511.                     user[users++] = cp;
  512.                 }
  513.             }
  514.             displayq(cbuf[0] - '\3');
  515.             exit(0);
  516.         case '\5':    /* remove a job from the queue */
  517.             printer = cp;
  518.             while (*cp && *cp != ' ')
  519.                 cp++;
  520.             if (!*cp)
  521.                 break;
  522.             *cp++ = '\0';
  523.             person = cp;
  524.             while (*cp) {
  525.                 if (*cp != ' ') {
  526.                     cp++;
  527.                     continue;
  528.                 }
  529.                 *cp++ = '\0';
  530.                 while (isspace(*cp))
  531.                     cp++;
  532.                 if (*cp == '\0')
  533.                     break;
  534.                 if (isdigit(*cp)) {
  535.                     if (requests >= MAXREQUESTS)
  536.                         fatal("Too many requests");
  537.                     requ[requests++] = atoi(cp);
  538.                 } else {
  539.                     if (users >= MAXUSERS)
  540.                         fatal("Too many users");
  541.                     user[users++] = cp;
  542.                 }
  543.             }
  544.             rmjob();
  545.             break;
  546.         }
  547.         fatal("Illegal service request");
  548. #ifndef sprite
  549.     }
  550. #endif
  551. }
  552.  
  553. /*
  554.  * Make a pass through the printcap database and start printing any
  555.  * files left from the last time the machine went down.
  556.  */
  557. startup()
  558. {
  559.     char buf[BUFSIZ];
  560.     register char *cp;
  561.     int pid;
  562.  
  563.     printer = buf;
  564.  
  565.     /*
  566.      * Restart the daemons.
  567.      */
  568.     while (getprent(buf) > 0) {
  569.         for (cp = buf; *cp; cp++)
  570.             if (*cp == '|' || *cp == ':') {
  571.                 *cp = '\0';
  572.                 break;
  573.             }
  574.         if ((pid = fork()) < 0) {
  575.             syslog(LOG_WARNING, "startup: cannot fork");
  576.             mcleanup();
  577.         }
  578.         if (!pid) {
  579.             endprent();
  580. #ifdef sprite
  581.             Proc_Detach(0);
  582. #endif
  583.             printjob();
  584.         }
  585.     }
  586. }
  587.  
  588. #define DUMMY ":nobody::"
  589.  
  590. /*
  591.  * Check to see if the from host has access to the line printer.
  592.  */
  593. chkhost(f)
  594.     struct sockaddr_in *f;
  595. {
  596.     register struct hostent *hp;
  597.     register FILE *hostf;
  598.     register char *cp, *sp;
  599.     char ahost[50];
  600.     int first = 1;
  601.     extern char *inet_ntoa();
  602.     int baselen = -1;
  603.  
  604.     f->sin_port = ntohs(f->sin_port);
  605.     if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
  606.         fatal("Malformed from address");
  607.     hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
  608.     if (hp == 0)
  609.         fatal("Host name for your address (%s) unknown",
  610.             inet_ntoa(f->sin_addr));
  611.  
  612.     strcpy(fromb, hp->h_name);
  613.     from = fromb;
  614.     if (!strcmp(from, host))
  615.         return;
  616.  
  617.     sp = fromb;
  618.     cp = ahost;
  619.     while (*sp) {
  620.         if (*sp == '.') {
  621.             if (baselen == -1)
  622.                 baselen = sp - fromb;
  623.             *cp++ = *sp++;
  624.         } else {
  625.             *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
  626.         }
  627.     }
  628.     *cp = '\0';
  629.     hostf = fopen("/etc/hosts.equiv", "r");
  630. again:
  631.     if (hostf) {
  632.         if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
  633.             (void) fclose(hostf);
  634.             return;
  635.         }
  636.         (void) fclose(hostf);
  637.     }
  638.     if (first == 1) {
  639.         first = 0;
  640.         hostf = fopen("/etc/hosts.lpd", "r");
  641.         goto again;
  642.     }
  643.     fatal("Your host does not have line printer access");
  644. }
  645.